#include "gpio.h"

void GPIO_Init(GPIO_Type *GPIOx, int GPIO_Pin_y, GPIO_Config *GPIO_InitStruct)
{
    // 输出引脚初始化
    if (GPIO_InitStruct->direction == output)
    {
        // 1. 配置引脚的方向寄存器为输出 bit[y]=1
        GPIOx->GDIR |= (1 << GPIO_Pin_y);
        // 2. 向输出引脚写入默认电平 bit[y]=level
        GPIO_Write(GPIOx, GPIO_Pin_y, (GPIO_InitStruct->level));
    }
    // 输入引脚初始化
    else
    {
        // 1. 配置引脚的方向寄存器为输入 bit[y]=0
        GPIOx->GDIR &= ~(1 << GPIO_Pin_y);
    }

    GPIO_InterruptModeConfig(GPIOx, GPIO_Pin_y,(GPIO_InitStruct->IRQMode));
}

// 向输出引脚写入具体数值
void GPIO_Write(GPIO_Type *GPIOx, int GPIO_Pin_y, int value)
{
    if (value == 0U)
    {
        GPIOx->DR &= ~(1 << GPIO_Pin_y);
    }
    else if (value == 1U)
    {
        GPIOx->DR |= (1 << GPIO_Pin_y);
    }

}

// 从输入引脚读取数值
int GPIO_Read(GPIO_Type *GPIOx, int GPIO_Pin_y)
{
    int value = 0;
    value = ((GPIOx->DR) >> GPIO_Pin_y) & 0x1;
    return value;
}

// 配置GPIO引脚中断使能 IMR寄存器bit[pin]:1--Enable 0--Disable
void GPIO_EnableIRQ(GPIO_Type *GPIOx, int GPIO_Pin_y)
{
    GPIOx->IMR |= (0x1 << GPIO_Pin_y);
}

// 禁止GPIO引脚中断功能 IMR寄存器bit[pin]:1--Enable 0--Disable
void GPIO_DisableIRQ(GPIO_Type *GPIOx, int GPIO_Pin_y)
{
    GPIOx->IMR &= ~(0x1 << GPIO_Pin_y);
}

// 清除中断状态 GPIOX_ISR寄存器bit[pin]:1
void GPIO_ClearIRQFlag(GPIO_Type *GPIOx, int GPIO_Pin_y)
{
    GPIOx->ISR |= (0x1 << GPIO_Pin_y);
}

// 配置引脚的中断触发方式 GPIOx_ICR1/GPIOx_ICR2寄存器
// GPIOx_ICR1寄存器控制GPIOx_IO00~GPIOx_IO15引脚
// GPIOx_ICR2寄存器控制GPIOx_IO16~GPIOx_IO31引脚

void GPIO_InterruptModeConfig(GPIO_Type *GPIOx, int GPIO_Pin_y, int mode)
{
    if(mode>3 || mode < 0)
    {
        return;
    }
    volatile unsigned int *ICR_Addr;
    int pin = GPIO_Pin_y;
    if (pin >= 16)
    {
        ICR_Addr = &(GPIOx->ICR2);
        pin -= 16;
    }
    else
    {
        ICR_Addr = &(GPIOx->ICR1);
    }
    
    switch (mode)
    {
    case GPIO_INT_LOW_LEVEL:
        *ICR_Addr &= ~(0x3<<(2*pin));
        break;
    case GPIO_INT_HIGH_LEVEL:
        *ICR_Addr |= (0x1<<(2*pin));
        break;
    case GPIO_INT_RISING_EDGE:
        *ICR_Addr &= ~(0x3<<(2*pin));
        *ICR_Addr |= (0x2<<(2*pin));
        break;
    case GPIO_INT_FALLING_EDGE:
        *ICR_Addr |= (0x3<<(2*pin));
        break;
    default:
        break;
    }
}
